<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>
<body>
    <script>
        /**构造函数定义与调用
        构造函数的首字母必须大写，用来区分于普通函数
        内部使用的 this 对象，来指向即将要生成的实例对象
        使用 new 运算符调用
        */
        /**
         * 实例化出的对象是独立
         * 比较两个对象是否相同要看引用
         * 实例化出来的两个对象 === false
         * 但是内存会有一定的浪费(行为相同，但是每个对象都要分配一个方法)
         * 我们可以将共有的方法(行为) 写在原型对象上
        */

        //原型链条  作用域链条
        var Person = function(){
            //...
        }
        var boy = new Person();
        console.log(boy.__proto__ === Person.prototype); //true
        var girl = new Person();
        console.log(girl.__proto__ === Person.prototype);//true
        // prototype 未来要实例化出来的对象的原型对象

        Person.prototype.constructor;
        Person.prototype.constructor.name; //'Person'
        boy.constructor.name; //'Person' 沿着原型链boy.__proto调用原型对象的constructor属性
        // 所以可以通过在Person.prototype(原型对象)上定义共有的行为(方法)；
        // 现在自身上找再在原型上去找

        var obj = {};
        obj.constructor.name; //'object';
        obj.__proto__.constructor.name; //'Object'
        var div = document.createElement("div");
        div.constructor.name; //'HTMLDivElement'
 
        /**例子*/
        function Person(name, age) {
            this.name = name;
            this.age = age;
            this.sayHello = function() {
                console.log(this.name + "say hello");
            };
        }
        var boy = new Person("bella", 23);
        var girl = new Person("Lucy", 20);
        console.log(boy.sayHello === girl.sayHello); //false

        /**
         * 在原型对象上添加sayHello方法：Person.prototype.sayHello
        */
        function Person(name, age) {
            this.name = name;
            this.age = age;
        }
        Person.prototype.sayHello = function() {
            console.log(this.name + " say hello");
        };
        var boy = new Person("Jack", 23);
        var girl = new Person("Lucy", 20);
        boy.sayHello();
        girl.sayHello();
        console.log(boy.sayHello === girl.sayHello); //true

        //回顾Array的原型方法是怎样定义的
        //Array.prototype.push
        //Array.prototype.pop
        //Array.prototype.filter
        //...

        //思考
        function Person(name, age) {
            this.name = name;
            this.age = age;
        }
        var boy = new Person("Jack", 23);
        var girl = new Person("Lucy", 20); 
        console.log(Person.prototype === boy.__proto__); //true
        console.log(Person.prototype === girl.__proto__);//true

        // 两种表示方法
        // Person.prototype.jump = function(){ //通过实例化对象构造器的原型对象调用jump方法
        boy.__proto__.jump = function(){
            console.log(this.name + " jump");
        }
        girl.jump(); //可以调用 通过__proto__调用jump方法 Lucy jump

        // 实例对象可以访问本身对象上的实例属性，也可以访问原型对象上的原型属性，
        // 但是不能访问构造函数上的静态属性。静态属性只能通过构造函数来访问。
        function People(name) {
            this.name = name;
        }
        // 静态属性只能通过构造函数来访问。
        People.index = 1; //静态属性
        People.prototype.hobby = "singing"; //原型属性
        // 实例对象可以访问本身对象上的实例属性，也可以访问原型对象上的原型属性，
        var per = new People("Tom");
        per.age = 20; //实例属性
        per.hobby;  //'singing'

        //静态方法案例
        console.log(Array.isArray([1]));
        console.log(Array.of("arrayLike"));
        console.log(Array.from("arrayLike"));
        //实例方法案例
        console.log(["a","r","r","a","y"].slice(0,2)); //直接用实例化的对象调用
        console.log(Array.prototype.slice.call(["a","r","r","a","y"],0,2));
        //slice方法在原型对象上 想要调用可以用call改变this指向 调用slice方法

        function Person(name, age) {
            this.name = name;
            this.age = age;
            this.sayHello = function() {
            console.log(this.name + "say hello");
            };
        }
        var boy = new Person("bella", 23);
        var girl = new Person("Lucy", 20);

        /**没有 return 语句，返回 this，即实例对象
        有 return 语句，且返回值为基本数据类型，构造函数会忽略 return 的值，依然返回 this 对象
        有 return 语句，且返回值为引用数据类型，构造函数会返回 return 的值*/

        // 将构造函数以参数形式传入
        function New(fun) {
            // 声明一个中间对象，该对象为最终返回的实例
            // 将实例的原型指向构造函数的原型
            // 将构造函数内部的 this 指向修改为指向实例对象
            // 当我们在构造函数中明确指定了返回对象时，那么new的执行结果就是该返回对象
            // 如果没有明确指定返回对象，则返回实例对象
        }

        // 模拟new实例化对象
        function New(func) {
            // 声明一个中间对象，该对象为最终返回的实例 （分配空间）
            var res = {};

            // 将实例的原型指向构造函数的原型
            res.__proto__ = func.prototype;

            // ret为构造函数执行的结果，这里通过apply （初始化）
            // 将构造函数内部的 this 指向修改为指向 res，即为实例对象
            var ret = func.apply(res, Array.prototype.slice.call(arguments, 1));

            // 当我们在构造函数中明确指定了返回对象时，那么new的执行结果就是该返回对象
            // 参考之前函数的几种return情况
            if ((typeof ret === "object" || typeof ret === "function") && ret !== null) {
                return ret;
            }
            // 如果没有明确指定返回对象，则默认返回res，这个res就是实例对象
            return res;
        }

        var boy = New(Person, "bella", 23);
        var girl = New(Person, "Lucy", 20);
        
    </script>
</body>
</html>